import $ from 'jquery';
import Button from './Button';
import Popover from '../Popover';

let slice = Array.prototype.slice;

export default class CodeButton extends Button {

  _init() {
    super._init.call(this);
    this.editor.on('decorate', (e, $el) => $el.find('pre').each((i, pre) => this.decorate($(pre))));
    return this.editor.on('undecorate', (e, $el) => $el.find('pre').each((i, pre) => this.undecorate($(pre))));
  }

  render() {
    let args;
    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
    super.render.apply(this, args);
    return this.popover = new CodePopover({ button: this });
  }

  _checkMode() {
    let range = this.editor.selection.range();
    if ($(range.cloneContents()).find(this.editor.util.blockNodes.join(',')) > 0 || (range.collapsed && this.editor.selection.startNodes().filter('code').length === 0)) {
      this.inlineMode = false;
      return this.htmlTag = 'pre';
    } else {
      this.inlineMode = true;
      return this.htmlTag = 'code';
    }
  }

  _status() {
    this._checkMode();
    super._status.call(this);
    if (this.inlineMode) {
      return;
    }
    if (this.active) {
      return this.popover.show(this.node);
    } else {
      return this.popover.hide();
    }
  }

  decorate($pre) {
    let $code;
    let lang;
    let ref;
    let ref1;
    $code = $pre.find('> code');
    if ($code.length > 0) {
      lang = (ref = $code.attr('class')) != null ? (ref1 = ref.match(/lang-(\S+)/)) != null ? ref1[1] : void 0 : void 0;
      $code.contents().unwrap();
      if (lang) {
        return $pre.attr('data-lang', lang);
      }
    }
  }

  undecorate($pre) {
    let $code;
    let lang;
    lang = $pre.attr('data-lang');
    $code = $('<code/>');
    if (lang && lang !== -1) {
      $code.addClass(`lang-${lang}`);
    }
    return $pre.wrapInner($code).removeAttr('data-lang');
  }

  command() {
    return this.inlineMode ? this._inlineCommand() : this._blockCommand();
  }

  _blockCommand() {
    let $rootNodes, clearCache, nodeCache, resultNodes;
    $rootNodes = this.editor.selection.rootNodes();
    nodeCache = [];
    resultNodes = [];
    clearCache = () => {
      let $pre;
      if (!(nodeCache.length > 0)) return;
      $pre = $(`<${this.htmlTag}/>`).insertBefore(nodeCache[0]).text(this.editor.formatter.clearHtml(nodeCache));
      resultNodes.push($pre[0]);
      return nodeCache.length = 0;
    };
    $rootNodes.each((i, node) => {
      let $node;
      let $p;
      $node = $(node);
      if ($node.is(this.htmlTag)) {
        clearCache();
        $p = $('<p/>').append($node.html().replace('\n', '<br/>')).replaceAll($node);
        return resultNodes.push($p[0]);
      }
      if ($node.is(this.disableTag) || this.editor.util.isDecoratedNode($node) || $node.is('blockquote')) return clearCache();
      return nodeCache.push(node);
    });
    clearCache();
    this.editor.selection.setRangeAtEndOf($(resultNodes).last());
    return this.editor.trigger('valuechanged');
  }

  _inlineCommand() {
    let $code;
    let $contents;
    let range;
    range = this.editor.selection.range();
    if (this.active) {
      range.selectNodeContents(this.node[0]);
      this.editor.selection.save(range);
      this.node.contents().unwrap();
      this.editor.selection.restore();
    } else {
      $contents = $(range.extractContents());
      $code = $(`<${this.htmlTag}/>`).append($contents.contents());
      range.insertNode($code[0]);
      range.selectNodeContents($code[0]);
      this.editor.selection.range(range);
    }
    return this.editor.trigger('valuechanged');
  }
}

CodeButton.prototype.name = 'code';
CodeButton.prototype.icon = 'code';
CodeButton.prototype.htmlTag = 'pre';
CodeButton.prototype.disableTag = 'ul, ol, table';

class CodePopover extends Popover {
  render() {
    this._tpl = `<div class="code-settings">\n  <div class="settings-field">\n    <select class="select-lang">\n      <option value="-1">${this._t('selectLanguage')}</option>\n    </select>\n  </div>\n</div>`;
    this.langs = this.editor.opts.codeLanguages || [{
      name: 'Bash',
      value: 'bash'
    }, {
        name: 'C++',
        value: 'c++'
      }, {
        name: 'C#',
        value: 'cs'
      }, {
        name: 'CSS',
        value: 'css'
      }, {
        name: 'Erlang',
        value: 'erlang'
      }, {
        name: 'Less',
        value: 'less'
      }, {
        name: 'Sass',
        value: 'sass'
      }, {
        name: 'Diff',
        value: 'diff'
      }, {
        name: 'CoffeeScript',
        value: 'coffeescript'
      }, {
        name: 'HTML,XML',
        value: 'html'
      }, {
        name: 'JSON',
        value: 'json'
      }, {
        name: 'Java',
        value: 'java'
      }, {
        name: 'JavaScript',
        value: 'js'
      }, {
        name: 'Markdown',
        value: 'markdown'
      }, {
        name: 'Objective C',
        value: 'oc'
      }, {
        name: 'PHP',
        value: 'php'
      }, {
        name: 'Perl',
        value: 'parl'
      }, {
        name: 'Python',
        value: 'python'
      }, {
        name: 'Ruby',
        value: 'ruby'
      }, {
        name: 'SQL',
        value: 'sql'
      }];
    this.el.addClass('code-popover').append(this._tpl);
    this.selectEl = this.el.find('.select-lang');
    this.langs.forEach((lang) => $('<option/>', {
      text: lang.name,
      value: lang.value
    }).appendTo(this.selectEl));
    this.selectEl.on('change', () => {
      let selected;
      this.lang = this.selectEl.val();
      selected = this.target.hasClass('selected');
      this.target.removeClass().removeAttr('data-lang');
      if (this.lang !== -1) this.target.attr('data-lang', this.lang);
      if (selected) this.target.addClass('selected');
      
      return this.editor.trigger('valuechanged');
    });
    return this.editor.on('valuechanged', () => {
      if (this.active) return this.refresh();
    });
  }

  show() {
    let args;
    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
    super.show.apply(this, args);
    this.lang = this.target.attr('data-lang');
    if (this.lang != null) {
      return this.selectEl.val(this.lang);
    } else {
      return this.selectEl.val(-1);
    }
  }
}